package com.hmdp.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.hmdp.constant.RedisConstants;
import com.hmdp.dto.Result;
import com.hmdp.dto.ScrollResult;
import com.hmdp.dto.UserDTO;
import com.hmdp.entity.Blog;
import com.hmdp.entity.Follow;
import com.hmdp.entity.User;
import com.hmdp.mapper.BlogMapper;
import com.hmdp.service.IBlogService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.service.IFollowService;
import com.hmdp.service.IUserService;
import com.hmdp.utils.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Service
@Slf4j
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {

    @Autowired
    private IUserService userService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private IFollowService followService;

    /**
     * 根据博客id查看博客
     * @param id
     * @return
     */
    @Override
    public Blog queryBlogById(Long id) {
        Blog blog = query().eq("id", id).one();
        if (blog == null) {
            throw new RuntimeException("笔记不存在");
        }
        User user = userService.query().eq("id", blog.getUserId()).one();
        if (user == null) {
            throw new RuntimeException("作者不存在");
        }
        blog.setIcon(user.getIcon());
        blog.setName(user.getNickName());
        blog.setIsLike(isLike(blog.getId()));
        return blog;
    }

    /**
     * 点赞
     * @param id
     */
    @Override
    public void likeBlog(Long id) {
        Long userId = UserHolder.getUser().getId();
        Blog blog = query().eq("id", id).one();
        //如果未点赞,点赞数+1
        if(!isLike(blog.getId())){
            stringRedisTemplate.opsForZSet().add(RedisConstants.BLOG_LIKED_KEY+id,
                                                    userId.toString(),
                                                    System.currentTimeMillis());
            update().setSql("liked = liked + 1").eq("id", id).update();
        }else{
            //如果点赞过，取消点赞，点赞数-1
            stringRedisTemplate.opsForZSet().remove(RedisConstants.BLOG_LIKED_KEY+id,
                                                       userId.toString());
            update().setSql("liked = liked - 1").eq("id", id).update();
        }
    }

    /**
     * 显示前五点赞用户
     * @param id
     * @return
     */
    @Override
    public List<UserDTO> queryBlogLikes(Long id) {
        Long userId = UserHolder.getUser().getId();
        Set<String> ids = stringRedisTemplate.opsForZSet().range(RedisConstants.BLOG_LIKED_KEY + id, 0, 4);
        List<Long> userIds = ids.stream().map(Long::valueOf).collect(Collectors.toList());
        if(userIds==null || userIds.size()==0){
            return Collections.emptyList();
        }
        String idstr = StrUtil.join(",", userIds);
        List<User> users = userService.query().in("id", userIds).last("order by field(id," + idstr + ")").list();
        List<UserDTO> userDTOList = new ArrayList<>();
        for(User user : users){
            UserDTO userDTO = new UserDTO();
            BeanUtils.copyProperties(user, userDTO);
            userDTOList.add(userDTO);
        }
        return userDTOList;
    }

    /**
     * 发布笔记
     * @param blog
     * @return
     */
    @Override
    public Long saveBlog(Blog blog) {
        Long userId = UserHolder.getUser().getId();
        blog.setUserId(userId);
        // 保存探店博文
        save(blog);
        Long blogId = blog.getId();
        //获取发布时时间戳
        double time = (double) System.currentTimeMillis();

        //推送给粉丝(发送到粉丝收件箱)
        //获取粉丝列表
        log.info("开始推送：{}",blogId);
        List<Follow> followers = followService.query().eq("follow_user_id", userId).list();
        List<Long> followerIds = followers.stream().map(Follow::getUserId).collect(Collectors.toList());
        log.debug("粉丝列表：{}",followerIds);
        for(Long followerId : followerIds){
            stringRedisTemplate.opsForZSet().add(RedisConstants.FEED_KEY+followerId,
                                                    blogId.toString(),
                                                    time);
        }
        return blogId;
    }

    /**
     * 接收关注博主推送(从自己的信箱中滚动分页读取消息)
     * @param max
     * @param offset
     * @return
     */
    @Override
    public ScrollResult queryBlogOfFollow(Long max, Integer offset) {
        log.info("读取信箱");
        log.debug("max: {},offset: {}", max, offset);
        Long userId = UserHolder.getUser().getId();
        //按照时间顺序(score)从大到小取出指定个数
        List<Blog> blogsOfFollow = new ArrayList<>();
        Set<ZSetOperations.TypedTuple<String>> blogs = stringRedisTemplate.opsForZSet().reverseRangeByScoreWithScores(RedisConstants.FEED_KEY + userId, 0,max, offset,2);
        log.debug("接受推送：{}",blogs);
        double min = (double) System.currentTimeMillis();
        int count=0;
        for(ZSetOperations.TypedTuple<String> blog : blogs){
            //记录最小时间及其个数
            Double time = blog.getScore();
            if(time==min){
                count++;
            }else{
                min = time;
                count = 1;
            }
            //收到的消息：笔记id
            String idstr = blog.getValue();
            Long blogId = Long.valueOf(idstr);
            Blog blogOfFollow = query().eq("id", blogId).one();
            blogsOfFollow.add(blogOfFollow);
        }
        //封装结果
        ScrollResult scrollResult = new ScrollResult();
        scrollResult.setList(blogsOfFollow);
        scrollResult.setMinTime((long) min);
        scrollResult.setOffset(count);
        return scrollResult;
    }

    /**
     * 查询当前用户是否给该博客点赞过
     * @param blogId
     * @return
     */
    public Boolean isLike(Long blogId) {
        Long userId = UserHolder.getUser().getId();
        Double likedTime = stringRedisTemplate.opsForZSet().score(RedisConstants.BLOG_LIKED_KEY + blogId, userId.toString());
        if (likedTime == null) {
            return false;
        }
        return true;
    }
}
